package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {


    @Autowired
    private RestHighLevelClient client;

    @Override
    public PageResult search(RequestParams param) {


            SearchRequest request = new SearchRequest("hotel");

            buildBasicQuery(param, request);


            //设置结果类型：排序

            //排序设置1：根据价格或评价排序
            if (StringUtils.isNotBlank(param.getSortBy()) && !param.getSortBy().equals("default")) {
                request.source().sort(param.getSortBy(), SortOrder.DESC);//默认都是降序
            }

            //排序设置2：根据我的定位由近及远排序
            if (StringUtils.isNotBlank(param.getLocation())) {
                request.source()
                        .sort(
                                SortBuilders.geoDistanceSort("location", new GeoPoint(param.getLocation()))//排序字段
                                        .order(SortOrder.ASC) //排序方向：由近及远排序
                                        .unit(DistanceUnit.KILOMETERS) //距离单位
                        );
            }

            Integer page = param.getPage();
            Integer size = param.getSize();
            request.source().from((page - 1) * size).size(size);

            //3. 发起搜索请求
            try {
                SearchResponse response = client.search(request, RequestOptions.DEFAULT);

                //4. 解析响应结果（从外到内，逐层解析）
                SearchHits hits = response.getHits();
                long total = hits.getTotalHits().value; //总条数

                SearchHit[] searchHits = hits.getHits();
                List<HotelDoc> hotels = new ArrayList<>();
                if (searchHits != null && searchHits.length > 0) {
                    for (SearchHit hit : searchHits) {
                        //原始的酒店JSON数据
                        String hotelJSON = hit.getSourceAsString();
                        HotelDoc hotelDoc = JSON.parseObject(hotelJSON, HotelDoc.class);

                        //获取距离对象
                        Object[] sortValues = hit.getSortValues();
                        if (sortValues != null && sortValues.length > 0) {
                            hotelDoc.setDistance(sortValues[0]); //酒店到我的距离，单位是KM
                        }
                        hotels.add(hotelDoc);
                    }
                }

                //5. 响应数据
                return new PageResult(total, hotels);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }



    private void buildBasicQuery(RequestParams param, SearchRequest request) {
        String key = param.getKey();

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        if (StringUtils.isBlank(key)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else{
            boolQuery.must(QueryBuilders.matchQuery("all",key));
        }

        if(StringUtils.isNotBlank(param.getCity())){
            boolQuery.filter(QueryBuilders.termQuery("city",param.getCity()));
        }

        if(StringUtils.isNotBlank(param.getBrand())){
            boolQuery.filter(QueryBuilders.termQuery("brand",param.getBrand()));
        }

        if(StringUtils.isNotBlank(param.getStarName())){
            boolQuery.filter(QueryBuilders.termQuery("starName",param.getStarName()));
        }

        if(param.getMaxPrice() !=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(param.getMaxPrice()));
        }

        if(param.getMinPrice() !=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gt(param.getMinPrice()));
        }

        //设置查询条件最终类型：算分函数查询（嵌套布尔查询）
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery( boolQuery,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10))
                });

        request.source().query(functionScoreQueryBuilder);


    }


}
